using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Newtonsoft.Json;
using jMVC.Utils;

namespace jMVC
{
    /// <summary>
    /// Utility class for serializing an object to JSON format using a single, shared JsonSerializer object
    /// into which you can register JsonConverters
    /// </summary>
    internal class JsonSerializationHelper
    {
        private JsonSerializer m_Serializer;
        public JsonSerializationHelper()
        {
            m_Serializer = new JsonSerializer();
            m_Serializer.Converters.Add(new DateTimeUTCConverter());
        }

        public void RegisterJsonConverter(JsonConverter converter)
        {
            m_Serializer.Converters.Add(converter);
        }

        public void WriteSerialized(object obj, TextWriter writer)
        {
            if (obj == null)
                writer.Write("null");
            else
                m_Serializer.Serialize(writer, obj);
        }

        public string Serialize(object obj)
        {
            if (obj == null)
                return null;
            else
            {
                StringWriter sw = new StringWriter();
                m_Serializer.Serialize(sw, obj);
                return sw.ToString();
            }
        }

        public object Deserialize(JsonReader reader, Type type)
        {
            if(type != null)
                return m_Serializer.Deserialize(reader, type);
            else
                return m_Serializer.Deserialize(reader);
        }

        public object Deserialize(string json, Type type)
        {
            if (string.IsNullOrEmpty(json))
                return null;
            else
            {

                if (type == null)
                    throw new ArgumentNullException("type");

                JsonReader reader = new JsonReader(new System.IO.StringReader(json));
                if (typeof(JavaScriptObject).IsAssignableFrom(type)) // Don't specify a type if it's really a JS object, otherwise Json.net throws errors
                    return m_Serializer.Deserialize(reader);
                else
                    return m_Serializer.Deserialize(reader, type);
            }
        }

        /// <summary>
        /// To avoid timezone problems, dates will be represented in UTC in Javascript
        /// </summary>
        private class DateTimeUTCConverter : JsonConverter
        {
            public override void WriteJson(JsonWriter writer, object value)
            {
                writer.WriteValue(((DateTime)value).ToUniversalTime());
            }

            public override object ReadJson(JsonReader reader, Type objectType)
            {
                if(reader.Value is string)
                    return DateTime.Parse((string)reader.Value);
                else if(reader.Value is DateTime)
                    return new DateTime(((DateTime)reader.Value).Ticks, DateTimeKind.Utc);
                else
                    throw new NotImplementedException("DateTimeUTCConverter can't deserialize value of type " + reader.ValueType.FullName);
            }

            public override bool CanConvert(Type objectType)
            {
                return typeof(DateTime).IsAssignableFrom(objectType);
            }
        }
    }
}
